/* Copyright 2008. All rights reserved. */
package org.bling.parser;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.antlr.runtime.ANTLRInputStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.tree.CommonTree;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.bling.language.BlingLexer;
import org.bling.language.BlingParser;
import org.bling.parser.tags.Tag;
import org.bling.parser.tags.TagFactory;
import org.bling.process.context.ScreenContext;
import org.bling.util.Util;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Widget;

/**
 * <code>BlingDocument</code> is the interface through which the framework
 * interacts with XML documents. The BlingDocument is responsible for the
 * loading of the file, parsing, and converting to SWT.
 * 
 * @author DISBROB
 * 
 */
public class BlingDocument {

	private static final Logger LOG = LogManager.getLogger(BlingDocument.class);
	Node bling;

	// FIXME: This should be fixed, so parsing exception is thrown at
	// constructor, not at toSWT
	public BlingDocument(Node bling) {
		this.bling = bling;
	}

	public BlingDocument(InputStream stream) throws IOException,
			RecognitionException {
		ANTLRInputStream input = new ANTLRInputStream(stream);
		BlingLexer lexer = new BlingLexer(input);
		CommonTokenStream tokens = new CommonTokenStream(lexer);
		BlingParser parser = new BlingParser(tokens);
		BlingParser.bling_return r = parser.bling();

		LOG.debug("Parsing BlingDocument: \n"
				+ Util.prettyPrint((CommonTree) r.getTree()));

		this.bling = new Node((CommonTree) r.getTree());

	}

	/*
	 * @deprecated
	 */
	public BlingDocument(StringBuffer bling) throws IOException,
			RecognitionException {
		this(bling.toString());
	}

	/*
	 * @deprecated
	 */
	public BlingDocument(String bling) throws IOException, RecognitionException {
		this(new ByteArrayInputStream(bling.getBytes()));
	}

	public void attach(Composite parent, Map<String, Object> contextParms)
			throws BlingParserException {
		ScreenContext context = new ScreenContext(parent);
		context.setAttributes(contextParms);

		if (bling == null) {
			throw new RuntimeException("Nothing to parse");
		}

		List<Node> children = bling.getChildren();
		if (children.size() != 1) {
			// FIXME: better error handling here, we are trying to ensure it
			// only has 1 child, bling. In the future, it is possible to have
			// <include> or other init level tags other than <bling>
			throw new RuntimeException(
					"Unsupported Tags Exception - expected <bling>");
		}

		Node tag = children.get(0);

		traverse(tag, context, null);

	}

	public Widget traverse(Node node, ScreenContext context, Tag parent)
			throws BlingParserException {

		Tag tag = null;

		// Execute the current node
		if (node.type == 6 || node.type == 8) {
			tag = TagFactory.get(node);
			tag.setParent(parent);
			tag.setContext(context);
		} else if (node.type == 9) {
			// FIXME: This is a problem... see, 9 is just regular plan text
			// content, and how to handle content like this is dependant upon
			// what the parent is. If the parent is <LABEL> then body content
			// should be fine, as it will be placed as the text of the label.
			// But if the parent is <bling> then appending to bodyContent isn't
			// good enough, becausee the text/label needs to be rendered in
			// sequence. For example <bling>Hello <button>PUSH</button>
			// World</bling> would display the components out of order
			String bodyContent = parent.getBodyContent();
			parent.setBodyContent(bodyContent + node.getName());
			return null;
		} else {
			throw new RuntimeException("Invalid node type = " + node.type);
		}

		if (tag == null) {
			throw new RuntimeException("This shouldn't happen - tag is null");
		}

		if (tag.doStartTag() == Tag.EVAL_PAGE) {

			do {
				// Execute it's children
				List<Node> children = node.getChildren();
				Iterator<Node> i = children.iterator();
				while (i.hasNext()) {
					Node child = i.next();
					traverse(child, context, tag);
				}
			} while (tag.doAfterBody() == Tag.EVAL_BODY_AGAIN);

			tag.doEndTag();
		}

		return tag.getWidget();
	}

}
